package com.leyou.auth.service;

import com.leyou.auth.config.JwtProperties;
import com.leyou.auth.dto.UserInfo;
import com.leyou.common.auth.domain.Payload;
import com.leyou.common.auth.utils.JwtUtils;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.common.utils.CookieUtils;
import com.leyou.user.UserClient;
import com.leyou.user.domain.UserDTO;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
public class AuthService {

    @Autowired
    private UserClient userClient;
    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private StringRedisTemplate redisTemplate;

    //登陆方法
    //加密用户信息 得到token  存在cookie 中返回 需要response
    //需要调用替他服务的方法
    public void login(String username, String password, HttpServletResponse response) {
        //调用接口中的方法 查询用户信息
        //不用判空 如为空在user服务中就报错了
        UserDTO userDTO = userClient.queryUser(username, password);
        //转成 userInfo 对象中没有封装 密码 为了安全
        UserInfo userInfo = new UserInfo(userDTO.getId(),userDTO.getUsername(),"admin");
        createToken(response, userInfo);
    }

    //产生一个token存入cookie返回
    private void createToken(HttpServletResponse response, UserInfo userInfo) {
        //创建token
        String token = JwtUtils.generateTokenExpireInMinutes(userInfo, jwtProperties.getPrivateKey(), jwtProperties.getUser().getExpire());
        //存到cookie中返回
        CookieUtils.newCookieBuilder().
                response(response).
                domain(jwtProperties.getUser().getCookieDomain()).
                name(jwtProperties.getUser().getCookieName()).
                value(token).
                httpOnly(true).
                build();
    }

    //回显用户数据 校验用户身份 每次刷新页面就会触发
    public UserInfo verify(HttpServletRequest request, HttpServletResponse response) {
        //用request 拿到 cookie中的token数据
        String token = CookieUtils.getCookieValue(request, jwtProperties.getUser().getCookieName());
        //查看用户是否登陆
        if(StringUtils.isBlank(token)){
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }
        //取出 载荷
        Payload<UserInfo> payload =null;
        try{
            //密钥校验
            //这里是为了防止用户在后台 擅自修改token 而造成 验证通过 需要解析 抛出异常
            payload = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey(), UserInfo.class);
        }catch (Exception e){
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }
        //先检测这个token是否存在于黑名单中 存在就直接抛异常 相当于登陆失败
        if(redisTemplate.hasKey(payload.getId())){
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }
        UserInfo userInfo = payload.getUserInfo();
        //需要添加 刷新机制  但是刷新也必须先是存在的合法的
        //过期时间
        Date expiration = payload.getExpiration();
        //得到刷新时间点
        DateTime refreshTime = new DateTime(expiration).minusMinutes(jwtProperties.getUser().getRefreshTime());
        //如果刷新时间点在当前时间的前面就刷新
        if(refreshTime.isBefore(System.currentTimeMillis())){
            createToken(response,userInfo);
        }
        //否则不刷新
        return userInfo;
    }

    //注销
    public void logout(HttpServletRequest request, HttpServletResponse response) {
        //获取token
        String token = CookieUtils.getCookieValue(request, jwtProperties.getUser().getCookieName());
        //验证
        Payload<Object> payload = null;
        try{
            payload = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey());
        }catch (Exception e){
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }
        //获取剩余过期时间
        Long remainTime = payload.getExpiration().getTime() - System.currentTimeMillis();
        //需要判断 剩下10秒就不需要拉入黑名单了
        if(remainTime > 10000){
            //先把token拉入黑名单 也就是redis  拉入黑名单之后需要在刷新里面判断
            redisTemplate.opsForValue().set(payload.getId(),"",remainTime, TimeUnit.MILLISECONDS);
        }
        //拉不拉入黑名单都要删除
        //删除
        CookieUtils.deleteCookie(jwtProperties.getUser().getCookieName(),
                jwtProperties.getUser().getCookieDomain(),response);

    }
}
